package handler;

import com.jme3.bullet.control.RigidBodyControl;
import com.jme3.bullet.joints.PhysicsJoint;
import com.jme3.collision.CollisionResult;
import com.jme3.collision.CollisionResults;
import com.jme3.input.InputManager;
import com.jme3.input.KeyInput;
import com.jme3.input.MouseInput;
import com.jme3.input.controls.ActionListener;
import com.jme3.input.controls.AnalogListener;
import com.jme3.input.controls.KeyTrigger;
import com.jme3.input.controls.MouseButtonTrigger;
import com.jme3.light.Light;
import com.jme3.math.Ray;
import com.jme3.scene.Geometry;
import com.jme3.scene.Spatial;
import com.jme3.scene.control.LightControl;
import control.character.MyPlayerControl;
import game.DataStore;

public class InputHandler implements ActionListener, AnalogListener {
    protected InputManager inputManager;
    protected MyPlayerControl playerControl;

    public InputHandler(InputManager inputManager, MyPlayerControl playerControl) {
	this.inputManager = inputManager;
	this.playerControl = playerControl;
	initInputs();
    }

    private void initInputs() {
	inputManager.addMapping("Left", new KeyTrigger(KeyInput.KEY_A));
	inputManager.addMapping("Right", new KeyTrigger(KeyInput.KEY_D));
	inputManager.addMapping("Up", new KeyTrigger(KeyInput.KEY_W));
	inputManager.addMapping("Down", new KeyTrigger(KeyInput.KEY_S));
	inputManager.addMapping("Jump", new KeyTrigger(KeyInput.KEY_SPACE));
	inputManager.addMapping("Use", new KeyTrigger(KeyInput.KEY_E));
        inputManager.addMapping("Sprint", new KeyTrigger(KeyInput.KEY_LSHIFT));
	inputManager.addMapping("Shoot", new MouseButtonTrigger(MouseInput.BUTTON_LEFT));
	
	inputManager.addListener(this, "Left", "Right", "Up", "Down", "Jump", "Use", "Sprint", "Shoot");
    }

    @Override
    public void onAnalog(String binding, float value, float tpf) {
	if (binding.equals("Left")) {
	    playerControl.setLeft(true);
	} else if (binding.equals("Right")) {
	    playerControl.setRight(true);
	} else if (binding.equals("Up")) {
	    playerControl.setUp(true);
	} else if (binding.equals("Down")) {
	    playerControl.setDown(true);
	}
    }

    @Override
    public void onAction(String binding, boolean keyPressed, float tpf) {
	if (binding.equals("Jump") && keyPressed) {
	    playerControl.jump();
	} else if (binding.equals("Sprint")) {
            if(keyPressed)
                playerControl.sprint(true);
            else
                playerControl.sprint(false);
        } else if (binding.equals("Use") && keyPressed) {
	    doUse();
	} else if (binding.equals("Shoot") && keyPressed) {
	    doShoot();
	}
    }

    public void doShoot() {
	CollisionResults results = new CollisionResults();
	Ray ray = new Ray(DataStore.cam.getLocation(), DataStore.cam.getDirection());
	DataStore.physicsHubNode.collideWith(ray, results);
	if (results.size() > 0) {
	  CollisionResult closest = results.getClosestCollision();
	  removeFromWorld(closest.getGeometry());
	}
    }

    public void removeFromWorld(Geometry geometry) {
	Spatial baseSpatial = geometry;
	while(baseSpatial.getControl(RigidBodyControl.class) == null)
	    baseSpatial = baseSpatial.getParent(); //this loop iterates up from a potential lower-level geometry to its first real physics parent
	RigidBodyControl control = baseSpatial.getControl(RigidBodyControl.class);
	for(PhysicsJoint joint : control.getJoints()) { //remove any attached joints
	    DataStore.bulletAppState.getPhysicsSpace().remove(joint);
	    joint.getBodyA().activate();
	    joint.getBodyB().activate();
	}
	LightControl lightControl = baseSpatial.getControl(LightControl.class);
	if(lightControl != null) { //remove any associated lights
	    Light light = lightControl.getLight();
	    DataStore.rootNode.removeLight(light);
	}
	baseSpatial.removeFromParent();
	DataStore.bulletAppState.getPhysicsSpace().remove(baseSpatial);
    }

    public void doUse() {
	if(playerControl.isHoldingObject()) {
	    playerControl.dropHeldObject();
	} else {
	    pickUpObject();
	}
    }

    public void pickUpObject() {
	CollisionResults results = new CollisionResults();
	Ray ray = new Ray(DataStore.cam.getLocation(), DataStore.cam.getDirection());
	DataStore.physicsHubNode.collideWith(ray, results);
	if (results.size() > 0) {
            CollisionResult closestResult = results.getClosestCollision();
            if(closestResult.getDistance() < 3f) {
                Spatial heldObject = closestResult.getGeometry();
                while(heldObject.getControl(RigidBodyControl.class) == null)
                    heldObject = heldObject.getParent(); //this loop iterates up from a potential lower-level geometry to its first real physics parent
                playerControl.setHeldObject(heldObject);
            }
	}
    }
}
